package com.rongji.dfish.tool;



import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileWriter;
import java.io.IOException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.Set;
import java.util.TreeSet;

import org.apache.bcel.classfile.ClassParser;
import org.apache.bcel.classfile.Constant;
import org.apache.bcel.classfile.ConstantClass;
import org.apache.bcel.classfile.ConstantUtf8;
import org.apache.bcel.classfile.JavaClass;

public class ReferenceLoopDetector {
	private static TreeSet<Reference> refernceSet=new TreeSet<Reference>();
	
	public static void main(String[] args) throws IOException{
		FileWriter fr=new FileWriter("./loopReference.txt");
//		File file=new File("D:/workspace/ciq/ciq_nopass/WebRoot/WEB-INF/classes");
		File file=new File("./WebRoot/WEB-INF/classes");
//		File file=new File("D:/server/tomcat6-pams/webapps/pam/WEB-INF/classes");
		
		System.out.println("searching reference ...");
		checkFolder(file);
		ingore();
//		checkReference();
//		onlyChekPackage();
		
		System.out.println("calculate reference loop ... ");
		Set<Loop> loops= finLoopRefs();
		System.out.println("calculate complete");
		for(Loop r:loops){
			System.out.println(r);
			fr.write(r.toString());
			fr.write("\r\n");
		}
		fr.close();
//		File file=new File("./WebRoot/WEB-INF/classes/com/rongji/itask/web/pub/view/ItaskStyle.class");
//		checkClass(file,"com/rongji/itask/web/pub/view/ItaskStyle");
		
	}
	private static long begin;
	private static long checkTimes;
	public static Set<Loop> finLoopRefs(){
		begin=System.currentTimeMillis();
		checkTimes=0;
		//预删除出度和入度为0的节点
		//没有必要对这些节点进行遍历。
		int oldSize=refernceSet.size();
		System.out.println("there are "+oldSize+" references.");
		while(true){
			HashSet<String> inSet=new HashSet<String>();
			HashSet<String> outSet=new HashSet<String>();
			for(Reference ref:refernceSet){
				inSet.add(ref.getClz());
				outSet.add(ref.getRefClz());
			}
			for(Iterator<Reference> iter=refernceSet.iterator();iter.hasNext();){
				Reference ref=iter.next();
				if(inSet.contains(ref.getRefClz())&&outSet.contains(ref.getClz())){
					continue;
				}
				iter.remove();
			}
			int size=refernceSet.size();
			if(oldSize==size){
				break;
			}else{
				oldSize=size;
			}
		}
		System.out.println("we should analysis  "+refernceSet.size()+" references.");
//		TreeSet<String> itemSet=new TreeSet<String>();
//		for(Reference ref:refernceSet){
//			itemSet.add(ref.getClz());
//		}
//		System.out.println(itemSet.size()+"items. "+itemSet);
		
		System.out.println("Pretreatment "+(System.currentTimeMillis()-begin)+"ms");
		
		//先准备一个快速查找下级的索引。
		HashMap<String,List<String>> accelerator=new HashMap<String,List<String>>();
		for(Reference ref:refernceSet){
			String clz=ref.getClz();
			List<String> refClzs=accelerator.get(clz);
			if(refClzs==null){
				refClzs=new ArrayList<String>();
				accelerator.put(clz, refClzs);
			}
			refClzs.add(ref.getRefClz());
		}
		Set<Loop> loops=new HashSet<Loop>();
		//随便找一个点深度优先遍历。找到所有的环，放在Result里面
		LinkedList<String> path=new LinkedList<String>();
		HashSet<String> checkedSet=new HashSet<String>();
		for(String c:accelerator.keySet()){
			if(checkedSet.contains(c)){
				continue;
			}
			checkLoop(c,path,loops,accelerator,checkedSet);
		}
		//再把其他点也放入检测。检测的时候，如果改点已经被检测过则自己忽略。
		//输出结构
		System.out.println("check for "+checkTimes+" times");
		System.out.println("cost "+(System.currentTimeMillis()-begin)+"ms");
		return loops;
	}


	private static void checkLoop(String c, LinkedList<String> path,Set<Loop> loops, HashMap<String, List<String>> accelerator,Set<String> checkedSet) {
		checkTimes++;
		if(path.contains(c)){
			//找到lloop
			loops.add(new Loop(path.subList(path.indexOf(c),path.size())));
			return;
		}
		path.add(c);
		List<String> subs=accelerator.get(c);
		if(subs!=null){
			for(String sub:subs){
				checkLoop(sub, path, loops, accelerator,checkedSet);
			}
		}
		checkedSet.add(c);
		path.removeLast();
	}
	private static void ingore() {
		//这些调用就当作合理，因为他们是包内调用
//		refernceSet.remove(new Reference("com/rongji/dfish/scheduler/business/StatefulActionExecutor", "com/rongji/dfish/scheduler/business/SchedulerMethods"));
//		refernceSet.remove(new Reference("com/rongji/dfish/scheduler/business/ActionExecutor", "com/rongji/dfish/scheduler/business/SchedulerMethods"));
//		refernceSet.remove(new Reference("com/rongji/itask/web/msg/engine/MsgToolCore", "com/rongji/itask/web/msg/engine/sender/impl/DBMsgSender"));
//		refernceSet.remove(new Reference("com/rongji/itask/web/msg/engine/MsgToolCore", "com/rongji/itask/web/msg/engine/sender/impl/SearchMsgSender"));
//		refernceSet.remove(new Reference("com/rongji/itask/commons/ItaskHelper", "com/rongji/itask/web/msg/engine/MsgToolCore"));//FIXME �?��修改代码
//		refernceSet.remove(new Reference("com/rongji/dfish/search/SearchingImpl", "com/rongji/dfish/search/ItaskBatchAdaptor"));
		
	}

	public static class Loop{
		private List<String> core=new ArrayList<String>();
		public Loop(List<String> list){
			//重新调整顺序
			String min=list.get(0);
			for(String s:list){
				if(min.compareTo(s)>0){
					min=s;
				}
			}
			int index=list.indexOf(min);
			core=new ArrayList<String>(list.subList(index, list.size()));
			core.addAll(list.subList(0, index));
		}
		public boolean equals(Object o){
			if(o==null){
				return false;
			}
			if(o==this){
				return true;
			}
			if(o instanceof Loop){
				return this.core.equals(((Loop)o).core);
			}
			return false;
		}
		public int hashCode(){
			return core.hashCode();
		}
		public String toString(){
			return core.toString();
		}
	}

	private static void checkFolder(File file) throws IOException {
		if(file.isDirectory()){
			for(File f:file.listFiles()){
				checkFolder(f);
			}
		}else if(file.getName().endsWith(".class")&&file.getName().indexOf('$')<0){
			checkFile(file);
		}
	}

	private static void checkFile(File file) throws IOException {
		String p=file.getAbsolutePath();
		int pos=p.indexOf("classes");
		String name=p.substring(pos+8,p.length()-6).replace('\\', '/');
		checkClass(file,name);
	}

	private static void checkClass(File file,String name) throws IOException {
		FileInputStream fis=new FileInputStream(file);
		org.apache.bcel.classfile.ClassParser parser =new ClassParser(fis,name);
		JavaClass jc=parser.parse();
		fis.close();
		Constant[] constants=jc.getConstantPool().getConstantPool();
		for(Constant con:constants) {
			if(con==null){continue;}
			if(con instanceof ConstantClass){
				ConstantClass cast=(ConstantClass)con;
				ConstantUtf8 cname= (ConstantUtf8) constants[cast.getNameIndex()];
				if(cname.getBytes().equals(name)){continue;}
				refernceSet.add(new Reference(name,cname.getBytes()));
			}
		}
	}
	public static class Reference implements java.lang.Comparable<Reference>{
		private String clz;
		public String getClz() {
			return clz;
		}
		public void setClz(String clz) {
			this.clz = clz;
		}
		public String getRefClz() {
			return refClz;
		}
		public void setRefClz(String refClz) {
			this.refClz = refClz;
		}
		private String refClz;
		public Reference(String clz,String refClz){
			this.clz=clz.intern();
			this.refClz=refClz.intern();
		}
		public String toString(){
			return clz+" --> "+refClz;
		}
		public int compareTo(Reference o) {
			int ch=this.clz.compareTo(o.clz);
			if(ch!=0)return ch;
			return this.refClz.compareTo(o.refClz);
		}
		
	}
}
